#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

#pragma pack(1) // 设置结构体对齐方式为1字节
// 定义OTA文件头的结构体
typedef struct {
    unsigned char identifier1;
    unsigned char identifier2;
    unsigned char firmware_count;
    unsigned char major_version;
    unsigned char minor_version;
    unsigned char reserved[11];
    int firmware_length;
    unsigned short checksum;
    char firmware_type[10];
} OTAHeader;

// 函数声明
void print_help();
int convert_hex_to_bin(const char *hex_file, const char *bin_file);
int convert_cly_to_bin(const char *cly_file, const char *bin_file);
void pack_file(const char *input_file, const char *type, const char *version);
void unpack_file(const char *input_file);
void view_file_info(const char *input_file);

int main(int argc, char *argv[]) {
    int opt;
    while ((opt = getopt(argc, argv, "hp:u:v:")) != -1) {
        switch (opt) {
            case 'h':
                print_help();
                break;
            case 'p':
                if (argc - optind < 2) {
                    fprintf(stderr, "Missing arguments for -p option.\n");
                    return 1;
                }
                pack_file(optarg, argv[optind], argv[optind + 1]);
                break;
            case 'u':
                unpack_file(optarg);
                break;
            case 'v':
                view_file_info(optarg);
                break;
            default:
                fprintf(stderr, "Usage: %s [-h] [-p file type version] [-u file] [-v file]\n", argv[0]);
                return 1;
        }
    }
    return 0;
}

// 打印帮助信息
void print_help() {
    printf("Usage: otapack.exe [-h] [-p file type version] [-u file] [-v file]\n");
    printf("Options:\n");
    printf("  -h            Show help information.\n");
    printf("  -p [file] [type] [version]  Pack the image file into an OTA upgrade package.\n");
    printf("  -u [file]      Unpack the OTA upgrade package into a bin image file.\n");
    printf("  -v [file]      View information about the OTA upgrade file.\n");
    printf("Supported file formats: .hex, .bin, .cly\n");
    printf("Supported firmware types: Any ASCII string up to 10 bytes.\n");
    printf("Firmware version format: a.b (a, b in the range of 0~255).\n");
}

// 将.hex文件转换为.bin文件
int convert_hex_to_bin(const char *hex_file, const char *bin_file) {
    // 这里可以调用hex2bin.exe工具进行转换
    char command[256];
    snprintf(command, sizeof(command), "hex2bin.exe %s %s", hex_file, bin_file);
    return system(command);
}

// 将.cly文件转换为.bin文件
int convert_cly_to_bin(const char *cly_file, const char *bin_file) {
    FILE *cly_fp = fopen(cly_file, "r");
    FILE *bin_fp = fopen(bin_file, "wb");
    if (cly_fp == NULL || bin_fp == NULL) {
        perror("Failed to open file");
        if (cly_fp) fclose(cly_fp);
        if (bin_fp) fclose(bin_fp);
        return -1;
    }

    // 读取程序长度
    char line[256];
    if (fgets(line, sizeof(line), cly_fp) == NULL) {
        perror("Failed to read program length");
        fclose(cly_fp);
        fclose(bin_fp);
        return -1;
    }
    int program_length = (int)strtol(line, NULL, 16);

    // 读取并转换后续行
    while (fgets(line, sizeof(line), cly_fp) != NULL) {
        for (int i = 0; i < 128; i += 2) {
            char byte_str[3] = {line[i], line[i + 1], '\0'};
            unsigned char byte = (unsigned char)strtol(byte_str, NULL, 16);
            fwrite(&byte, 1, 1, bin_fp);
        }
    }

    fclose(cly_fp);
    fclose(bin_fp);
    return 0;
}

// 打包文件
void pack_file(const char *input_file, const char *type, const char *version) {
    char bin_file[256];
    char ota_file[256];
    strcpy(bin_file, input_file);
    char *ext = strrchr(bin_file, '.');
    if (ext != NULL) {
        strcpy(ext, ".bin");
    }

    // 根据文件扩展名进行转换
    if (strcmp(ext, ".hex") == 0) {
        convert_hex_to_bin(input_file, bin_file);
    } else if (strcmp(ext, ".cly") == 0) {
        convert_cly_to_bin(input_file, bin_file);
    } else if (strcmp(ext, ".bin") != 0) {
        fprintf(stderr, "Unsupported file format.\n");
        return;
    }

    // 计算文件名
    strcpy(ota_file, input_file);
    ext = strrchr(ota_file, '.');
    if (ext != NULL) {
        *ext = '\0';
    }
    strcat(ota_file, "_ota.bin");

    // 打开文件
    FILE *bin_fp = fopen(bin_file, "rb");
    FILE *ota_fp = fopen(ota_file, "wb");
    if (bin_fp == NULL || ota_fp == NULL) {
        perror("Failed to open file");
        if (bin_fp) fclose(bin_fp);
        if (ota_fp) fclose(ota_fp);
        return;
    }

    // 计算文件长度和校验和
    fseek(bin_fp, 0, SEEK_END);
    int firmware_length = ftell(bin_fp);
    fseek(bin_fp, 0, SEEK_SET);
    unsigned short checksum = 0;
    unsigned char byte;
    while (fread(&byte, 1, 1, bin_fp) == 1) {
        checksum += byte;
    }
    fseek(bin_fp, 0, SEEK_SET);

    // 解析版本号
    unsigned char major_version = (unsigned char)atoi(strtok((char *)version, "."));
    unsigned char minor_version = (unsigned char)atoi(strtok(NULL, "."));

    // 填充OTA文件头
    OTAHeader header;
    header.identifier1 = 0xAA;
    header.identifier2 = 0x55;
    header.firmware_count = 1;
    header.major_version = major_version;
    header.minor_version = minor_version;
    memset(header.reserved, 0, sizeof(header.reserved));
    header.firmware_length = firmware_length;
    header.checksum = checksum;
    strncpy(header.firmware_type, type, sizeof(header.firmware_type));

    // 写入OTA文件头
    fwrite(&header, sizeof(OTAHeader), 1, ota_fp);

    // 写入固件镜像
    while (fread(&byte, 1, 1, bin_fp) == 1) {
        fwrite(&byte, 1, 1, ota_fp);
    }

    fclose(bin_fp);
    fclose(ota_fp);
}

// 解包文件
void unpack_file(const char *input_file) {
    FILE *ota_fp = fopen(input_file, "rb");
    if (ota_fp == NULL) {
        perror("Failed to open file");
        return;
    }

    // 读取OTA文件头
    OTAHeader header;
    fread(&header, sizeof(OTAHeader), 1, ota_fp);

    // 计算文件名
    char bin_file[256];
    strcpy(bin_file, input_file);
    char *ext = strrchr(bin_file, '.');
    if (ext != NULL) {
        *ext = '\0';
    }
    strcat(bin_file, "_unpacked.bin");

    // 打开输出文件
    FILE *bin_fp = fopen(bin_file, "wb");
    if (bin_fp == NULL) {
        perror("Failed to open file");
        fclose(ota_fp);
        return;
    }

    // 写入固件镜像
    unsigned char byte;
    for (int i = 0; i < header.firmware_length; i++) {
        if (fread(&byte, 1, 1, ota_fp) == 1) {
            fwrite(&byte, 1, 1, bin_fp);
        }
    }

    fclose(ota_fp);
    fclose(bin_fp);
}

// 查看文件信息
void view_file_info(const char *input_file) {
    FILE *ota_fp = fopen(input_file, "rb");
    if (ota_fp == NULL) {
        perror("Failed to open file");
        return;
    }

    // 读取OTA文件头
    OTAHeader header;
    fread(&header, sizeof(OTAHeader), 1, ota_fp);

    // 打印文件信息
    printf("Identifier: 0x%02X 0x%02X\n", header.identifier1, header.identifier2);
    printf("Firmware count: %d\n", header.firmware_count);
    printf("Firmware version: %d.%d\n", header.major_version, header.minor_version);
    printf("Firmware length: %d bytes\n", header.firmware_length);
    printf("Firmware checksum: 0x%04X\n", header.checksum);
    printf("Firmware type: %s\n", header.firmware_type);

    fclose(ota_fp);
}
